
{***********************************************************}
{                                                           }
{      Informix Data Access Components                      }
{                                                           }
{      TIfxTable component                                  }
{                                                           }
{      Copyright (c) 2001-2008 Luxena Software Company      }
{      http://www.luxena.com                                }
{      e-mail: support@luxena.com                           }
{                                                           }
{***********************************************************}

unit IfxTable;

{$I lx.inc}

interface

uses
  Classes, SysUtils, DB, LXSysUtils, LXDataSetImpl, IfxSQL, IfxCustomDataSet;


const

{ Property Default Values }

  IFX_DEFAULT_DEFAULT_INDEX = True;
  IFX_DEFAULT_READ_ONLY = False;
  IFX_DEFAULT_STORE_DEFS = False;


type

{ TIfxTable }

  TIfxTable = class(TIfxDesignDataSet)
  private
    FTableNameOriginal: string;
    FDatabaseName: string;
    FOwnerName: string;
    FTableName: string;
    FDefaultIndex: Boolean;
    FIndexDefs: TIndexDefs;
    FFieldsIndex: Boolean;
    FIndexName: string;
    FIndexFields: TStrings;
    FReadOnly: Boolean;
    FStoreDefs: Boolean;

    function CreateTableInfoSQL(const SQL: string): TIfxSQL; overload;

    function GetExists: Boolean;
    function GetIndexFieldCount: Integer;
    function GetIndexField(Index: Integer): TField;
    function GetIndexFieldNames: string;
    function GetIndexName: string;
    function GetMasterFields: string;

    procedure SetIndex(const Value: string; FieldsIndex: Boolean);
    procedure SetIndexDefs(Value: TIndexDefs);
    procedure SetIndexField(Index: Integer; const Value: TField);
    procedure SetIndexFieldNames(const Value: string);
    procedure SetIndexName(const Value: string);
    procedure SetMasterFields(const Value: string);
    procedure SetReadOnly(Value: Boolean);
    procedure SetTableName(const Value: string);

    function DatabaseQualifier: string;
    function QualifiedTableName: string;

    function IsFieldDefsStored: Boolean;
    function IsIndexDefsStored: Boolean;

    procedure CheckTableName;
    procedure CheckTableSQL;
    procedure ResetTableSQL;
    procedure CheckIndexFields;
    function GetMasterFieldList: TList;

    function FindIndex(Option: TIndexOption; var Index: TIndexDef): Boolean;
    function FindDefaultIndex(var Index: TIndexDef): Boolean;
    function FormatFieldList(const Fields, DescFields: string): string;

  protected
    procedure InternalInitFieldDefs; override;
    procedure DoOnNewRecord; override;
    function GetCanModify: Boolean; override;
    procedure UpdateIndexDefs; override;
    procedure DefChanged(Sender: TObject); override;
    procedure SetFilterText(const Value: string); override;
    procedure SetFilterOptions(Value: TFilterOptions); override;
    procedure PrepareCursor; override;
    procedure FilteredChanged; override;
    function CreateDataLink: TDataLink; override;
    procedure SetMasterParams; override;

    { IProviderSupport }
    function PSGetDefaultOrder: TIndexDef; override;
    function PSGetKeyFields: string; override;
    function PSGetTableName: string; override;
    function PSGetIndexDefs(IndexTypes: TIndexOptions): TIndexDefs; override;
    procedure PSSetCommandText(const CommandText: string); override;

  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure GetDetailLinkFields(MasterFields, DetailFields: TList); override;

    procedure CreateTable;
    procedure EmptyTable;
    procedure DeleteTable;

    procedure GetIndexNames(List: TStrings);
    procedure AddIndex(const Name, Fields: string; Options: TIndexOptions; const DescFields: string = '');
    procedure DeleteIndex(const Name: string);

    procedure GotoCurrent(Table: TIfxTable);

    property Exists: Boolean read GetExists;
    property IndexFieldCount: Integer read GetIndexFieldCount;
    property IndexFields[Index: Integer]: TField read GetIndexField write SetIndexField;

  published
    property DefaultIndex: Boolean read FDefaultIndex write FDefaultIndex default IFX_DEFAULT_DEFAULT_INDEX;
    property FieldDefs stored IsFieldDefsStored;
    property Filter;
    property FilterOptions;
    property IndexDefs: TIndexDefs read FIndexDefs write SetIndexDefs stored IsIndexDefsStored;
    property IndexFieldNames: string read GetIndexFieldNames write SetIndexFieldNames;
    property IndexName: string read GetIndexName write SetIndexName;
    property MasterFields: string read GetMasterFields write SetMasterFields;
    property MasterSource: TDataSource read GetDataSource write SetDataSource;
    property ReadOnly: Boolean read FReadOnly write SetReadOnly default IFX_DEFAULT_READ_ONLY;
    property StoreDefs: Boolean read FStoreDefs write FStoreDefs default IFX_DEFAULT_STORE_DEFS;
    property TableName: string read FTableNameOriginal write SetTableName;
    property UpdateObject write SetUpdateObject;
  end;


implementation

uses
  LXConsts, IfxTypes, IfxSqlTypes, IfxVarChar, IfxConsts, Ifx;


type

{ TIfxMasterDataLink }

  TIfxMasterDataLink = class(TDetailDataLink)
  private
    FTable: TIfxTable;
    FFieldNames: string;
    FFields: TList;
    procedure MasterChanged;
    procedure SetFieldNames(const Value: string);
  protected
    procedure ActiveChanged; override;
    procedure CheckBrowseMode; override;
    procedure LayoutChanged; override;
    procedure RecordChanged(Field: TField); override;
    function GetDetailDataSet: TDataSet; override;
  public
    constructor Create(Table: TIfxTable);
    destructor Destroy; override;
    property FieldNames: string read FFieldNames write SetFieldNames;
    property Fields: TList read FFields;
  end;

  
const
  TABLE_CREATE_SQL = 'create table %s (%s)';

  TABLE_ADD_PRIMARY_KEY_SQL =
    'alter table %1:s add constraint primary key (%2:s) constraint %0:s';

  TABLE_CREATE_UNIQUE_INDEX_SQL =
    'create unique index %s on %s (%s)';

  TABLE_CREATE_INDEX_SQL =
    'create index %s on %s (%s)';

  TABLE_DROP_SQL = 'drop table %s';

  TABLE_DROP_INDEX_SQL = 'drop index %s';

  TABLE_SELECT_SQL =
    'select * from %s';

  TABLE_TABID_SQL =
    'select ' +
      'nvl(s.btabid, t.tabid) ' +
    'from ' +
      '%"database"informix.systables t, ' +
      'outer %"database"informix.syssyntable s ' +
    'where ' +
      '%"ownerfilter"t.tabname = :tabname ' +
      'and s.tabid = t.tabid';

  TABLE_COLUMNS_SQL =
    'select ' +
      'c.colname, c.coltype, c.collength, %colxid colxid, d.type deftype ' +
    'from ' +
      '%"database"informix.syscolumns c, outer %"database"informix.sysdefaults d ' +
    'where ' +
      'c.tabid = (' + TABLE_TABID_SQL + ') ' +
      'and d.tabid = c.tabid ' +
      'and d.colno = c.colno';
  TABLE_FAMILY_XID_COLUMN: array [Boolean] of string =
    ('0', 'c.extended_id');

  TABLE_INDICES_SQL =
    'select ' +
      'si.idxname, ' +
      'sc.idxname pkname, ' +
      'si.idxtype, ' +
      'si.part1, si.part2, si.part3, si.part4, si.part5, si.part6, si.part7, si.part8%onlineindices ' +
    'from ' +
      '%"database"informix.sysindexes si, ' +
      'outer %"database"informix.sysconstraints sc ' +
    'where ' +
      'si.tabid = (' + TABLE_TABID_SQL + ') ' +
      'and sc.tabid = si.tabid ' +
      'and sc.constrtype = ''P''';
  TABLE_FAMILY_INDEX_COLUMNS: array [Boolean] of string =
  (
    '',
    ', si.part9, si.part10, si.part11, si.part12, si.part13, si.part14, si.part15, si.part16'
  );
  TABLE_INDEX_COLNO_BEGIN = 3;
  TABLE_FAMILY_INDEX_COLNO_END: array [Boolean] of Integer = (10, 18);

  TABLE_EMPTY_SQL = 'delete from %s';


{ TIfxMasterDataLink }

constructor TIfxMasterDataLink.Create(Table: TIfxTable);
begin
  inherited Create;
  FTable := Table;
  FFields := TList.Create;
end;

destructor TIfxMasterDataLink.Destroy;
begin
  FFields.Free;
  inherited;
end;

procedure TIfxMasterDataLink.ActiveChanged;
begin
  if (FFieldNames <> '') and not (csDestroying in FTable.ComponentState) then
    MasterChanged;
end;

procedure TIfxMasterDataLink.CheckBrowseMode;
begin
  if FTable.Active then
    FTable.CheckBrowseMode;
end;

procedure TIfxMasterDataLink.LayoutChanged;
begin
  ActiveChanged;
end;

procedure TIfxMasterDataLink.RecordChanged(Field: TField);

  function IsMasterChanged: Boolean;
  var
    I: Integer;
    MasterField: TField;
    DetailField: TField;
  begin
    for I := 0 to FFields.Count - 1 do
    begin
      MasterField := FFields[I];
      DetailField := FTable.IndexFields[I];
      if MasterField.Value <> DetailField.Value then
      begin
        Result := True;
        Exit;
      end;
    end;
    Result := False;
  end;

begin
  if FTable.Active and (DataSource.State <> dsSetKey) and IsMasterChanged then
  begin
    FTable.SetMasterParams;
    FTable.Requery;
  end;
end;

function TIfxMasterDataLink.GetDetailDataSet: TDataSet;
begin
  Result := FTable;
end;

procedure TIfxMasterDataLink.MasterChanged;
begin
  FFields.Clear;
  if Active then
    try
      DataSet.GetFieldList(FFields, FFieldNames);
    except
      FFields.Clear;
      raise;
    end;
    
  FTable.ResetTableSQL;
end;

procedure TIfxMasterDataLink.SetFieldNames(const Value: string);
begin
  if FFieldNames <> Value then
  begin
    FFieldNames := Value;
    MasterChanged;
  end;
end;


{ TIfxTable }

constructor TIfxTable.Create(AOwner: TComponent);
begin
  inherited;

  RequestLive := True;
  IfxSQL.IgnoreDataEvent := True;

  FDefaultIndex := IFX_DEFAULT_DEFAULT_INDEX;
  FIndexDefs := TIndexDefs.Create(Self);
  FReadOnly := IFX_DEFAULT_READ_ONLY;
  FStoreDefs := IFX_DEFAULT_STORE_DEFS;
end;

destructor TIfxTable.Destroy;
begin
  FIndexDefs.Free;
  FIndexFields.Free;

  inherited;
end;

function TIfxTable.CreateTableInfoSQL(const SQL: string): TIfxSQL;
begin
  CheckTableName;           

  CheckConnection.Open;

  Result := CreateInternalSQL(SQL);
  try
    Result.Macros.Values['database'] := DatabaseQualifier;
    if Connection.IsAnsiDatabase then
    begin
      Result.Macros.Values['ownerfilter'] := 't.owner = :owner and ';
      if FOwnerName = '' then
        Result.ParamByName('owner').AsChar := Connection.SessionUser else
        Result.ParamByName('owner').AsChar := FOwnerName;
    end;
    Result.ParamByName('tabname').AsChar := FTableName;
  except
    Result.Free;
    raise;
  end;
end;

function TIfxTable.GetExists: Boolean;
begin
  if FTableName = '' then
    Result := False

  else if Active then
    Result := True

  else
    with CreateTableInfoSQL(TABLE_TABID_SQL) do
      try
        Result := Execute <> 0;
      finally
        Free;
      end;
end;

function TIfxTable.GetIndexFieldCount: Integer;
begin
  CheckIndexFields;
  Result := FIndexFields.Count;
end;

function TIfxTable.GetIndexField(Index: Integer): TField;
begin
  CheckIndexFields;
  Result := FieldByName(FIndexFields[Index]);
end;

function TIfxTable.GetIndexFieldNames: string;
begin
  if FFieldsIndex then
    Result := FIndexName else
    Result := '';
end;

function TIfxTable.GetIndexName: string;
begin
  if FFieldsIndex then
    Result := '' else
    Result := FIndexName;
end;

function TIfxTable.GetMasterFields: string;
begin
  Result := TIfxMasterDataLink(DataLink).FieldNames;
end;

procedure TIfxTable.SetIndex(const Value: string; FieldsIndex: Boolean);
begin
  if Active then
    CheckBrowseMode;

  if (FIndexName <> Value) or (FFieldsIndex <> FieldsIndex) then
  begin
    FIndexName := Value;
    FFieldsIndex := FieldsIndex;
    FreeAndNil(FIndexFields);

    if FDefaultIndex or DataLink.Active then
      ResetTableSQL;
  end;
end;

procedure TIfxTable.SetIndexDefs(Value: TIndexDefs);
begin
  FIndexDefs.Assign(Value);
end;

procedure TIfxTable.SetIndexField(Index: Integer; const Value: TField);
begin
  GetIndexField(Index).Assign(Value);
end;

procedure TIfxTable.SetIndexFieldNames(const Value: string);
begin
  SetIndex(Value, Value <> '');
end;

procedure TIfxTable.SetIndexName(const Value: string);
begin
  SetIndex(Value, False);
end;

procedure TIfxTable.SetMasterFields(const Value: string);
begin
  TIfxMasterDataLink(DataLink).FieldNames := Value;
end;

procedure TIfxTable.SetReadOnly(Value: Boolean);
begin
  CheckInactive;
  FReadOnly := Value;
end;

procedure TIfxTable.SetTableName(const Value: string);

  procedure SetQualifiedTableName;
  var
    ColonIndex: Integer;
    DotIndex: Integer;
  begin
    FTableNameOriginal := Value;

    ColonIndex := Pos(':', Value);
    DotIndex := Pos('.', Value);

    if ColonIndex > 0 then
      FDatabaseName := Trim(Copy(Value, 1, ColonIndex - 1)) else
      FDatabaseName := '';

    if DotIndex > 0 then
    begin
      FOwnerName := Trim(Copy(Value, ColonIndex + 1, DotIndex - ColonIndex - 1));
      FTableName := Trim(Copy(Value, DotIndex + 1, Length(Value) - DotIndex));
    end else
    begin
      FTableName := Trim(Copy(Value, ColonIndex + 1, Length(Value) - ColonIndex));
      FOwnerName := '';
    end;
  end;

begin
  if FTableNameOriginal <> Value then
    if csReading in ComponentState then
      SetQualifiedTableName else
    begin
      CheckInactive;
      IfxSQL.SQL.Clear;
      SetQualifiedTableName;
      FIndexDefs.Clear;
      FIndexName := '';
      FreeAndNil(FIndexFields);
      DataEvent(dePropertyChange, 0);
    end;
end;

function TIfxTable.DatabaseQualifier: string;
begin
  if FDatabaseName <> '' then
    Result := FDatabaseName + '::';
end;

function TIfxTable.QualifiedTableName: string;
begin
  Result := DatabaseQualifier;
  if FOwnerName <> '' then
    Result := Result + FOwnerName + '.';
  Result := Result + FTableName;
end;

function TIfxTable.IsFieldDefsStored: Boolean;
begin
  Result := FStoreDefs and (FieldDefs.Count > 0);
end;

function TIfxTable.IsIndexDefsStored: Boolean;
begin
  Result := FStoreDefs and (FIndexDefs.Count > 0);
end;

procedure TIfxTable.CheckTableName;
begin
  if FTableName = '' then
    IDACError(SNoTableName, Self);
end;

procedure TIfxTable.CheckTableSQL;
var
  TableSQL: string;
  WhereFilter: string;
  OrderFields: string;
  I: Integer;
begin
  if IfxSQL.SQL.Count <> 0 then Exit;

  { User's filter }
  if Filtered and (Filter <> '') then
    WhereFilter := Filter;

  { Master-detail filter }
  if DataLink.Active and (MasterFields <> '') then
  begin
    CheckIndexFields;
    if FIndexFields.Count < GetMasterFieldList.Count then
      IDACError(SMasterDetailFieldsMismatch, Self);
    for I := 0 to GetMasterFieldList.Count - 1 do
    begin
      if WhereFilter <> '' then
        WhereFilter := WhereFilter + ' and ';
      WhereFilter := WhereFilter + FIndexFields[I] + ' = :' + FIndexFields[I];
    end;
  end;

  { Order field list }
  if FDefaultIndex then
  begin
    CheckIndexFields;
    for I := 0 to FIndexFields.Count - 1 do
    begin
      if I > 0 then
        OrderFields := OrderFields + ', ';
      OrderFields := OrderFields + FIndexFields[I];
      if FIndexFields.Objects[I] <> nil then
        OrderFields := OrderFields + ' desc';
    end;
  end;

  { Compile complete SQL }
  TableSQL := Format(TABLE_SELECT_SQL, [QualifiedTableName]);

  if WhereFilter <> '' then
    TableSQL := TableSQL + ' where ' + WhereFilter;

  if OrderFields <> '' then
    TableSQL := TableSQL + ' order by ' + OrderFields;

  IfxSQL.SQL.Text := TableSQL;
end;

procedure TIfxTable.ResetTableSQL;
begin
  if not Active then
    IfxSQL.SQL.Clear else
  begin
    CheckBrowseMode;
    DisableControls;
    try
      IfxSQL.SQL.Clear;
      Open;
    finally
      EnableControls;
    end;
  end;
end;

procedure TIfxTable.CheckIndexFields;
var
  Index: TIndexDef;
  FieldNames: string;
  DescFields: string;
  Pos: Integer;
begin
  if FIndexFields = nil then
  begin
    if not FindDefaultIndex(Index) then
      FieldNames := IndexFieldNames else
    begin
      FieldNames := Index.Fields;
      DescFields := Index.DescFields;
    end;
    FIndexFields := TStringList.Create;
    with FIndexFields do
      try
        Pos := 1;
        while Pos <= Length(FieldNames) do
          Add(ExtractFieldName(FieldNames, Pos));
        Pos := 1;
        while Pos <= Length(DescFields) do
          Objects[IndexOf(ExtractFieldName(DescFields, Pos))] := TObject(1);
      except
        FreeAndNil(FIndexFields);
        raise;
      end;
  end;
end;

function TIfxTable.GetMasterFieldList: TList;
begin
  Result := TIfxMasterDataLink(DataLink).Fields;
end;

function TIfxTable.FindIndex(Option: TIndexOption; var Index: TIndexDef): Boolean;
var
  I: Integer;
begin
  for I := 0 to FIndexDefs.Count - 1 do
    if Option in FIndexDefs[I].Options then
    begin
      Index := FIndexDefs[I];
      Result := True;
      Exit;
    end;

  Index := nil;
  Result := False;
end;

function TIfxTable.FindDefaultIndex(var Index: TIndexDef): Boolean;
begin
  FIndexDefs.Update;

  if FIndexName = '' then
    Result := FindIndex(ixPrimary, Index) or FindIndex(ixUnique, Index) else
  begin
    if FFieldsIndex then
      Index := FIndexDefs.GetIndexForFields(FIndexName, False) else
      Index := FIndexDefs.Find(FIndexName);
    Result := Index <> nil;
  end;
end;

function TIfxTable.FormatFieldList(const Fields, DescFields: string): string;

  function IsDescField(const Field: string): Boolean;
  var
    Pos: Integer;
  begin
    Pos := 1;
    while Pos <= Length(DescFields) do
      if Field = LowerCase(ExtractFieldName(DescFields, Pos)) then Break;
    Result := Pos <= Length(DescFields);
  end;

var
  Pos: Integer;
  Field: string;
begin
  Pos := 1;
  while Pos <= Length(Fields) do
  begin
    Field := ExtractFieldName(Fields, Pos);
    if Result <> '' then
      Result := Result + ', ' + Field else
      Result := Field;
    if IsDescField(LowerCase(Field)) then
      Result := Result + ' desc';
  end;
end;

procedure TIfxTable.InternalInitFieldDefs;
var
  IfxSQL: TIfxSQL;
  FieldNo: Integer;
  FieldDef: TFieldDef;
  sqlname: string;
  sqltype: int2;
  sqllen: int4;
  sqlxid: int4;
begin
  FieldDefs.BeginUpdate;
  try
    FieldDefs.Clear;

    IfxSQL := CreateTableInfoSQL(TABLE_COLUMNS_SQL);
    try
      IfxSQL.Macros.Values['colxid'] :=
        TABLE_FAMILY_XID_COLUMN[Connection.IsUniversalServer];
      IfxSQL.Open;
      FieldNo := FieldNoOfs;
      while not IfxSQL.EOF do
      begin
        FieldDef := FieldDefs.AddFieldDef;
        sqlname := Trim(IfxSQL.Fields[0].AsString);
        sqltype := IfxSQL.Fields[1].AsSmallInt;
        sqllen := IfxSQL.Fields[2].AsInteger;
        sqlxid := IfxSQL.Fields[3].AsInteger;
        TIfxField.AdjustType(sqltype, sqllen, sqlxid, True);
        ActiveFieldTypes.InitFieldDef(FieldDef, FieldNo, sqlname, sqltype,
          sqllen, sqlxid);
        if not IfxSQL.Fields[4].IsNull then
          FieldDef.Required := False;
        IfxSQL.Next;
        Inc(FieldNo);
      end;
    finally
      IfxSQL.Free;
    end;

    if FieldDefs.HiddenFields then
    begin
      FieldDef := FieldDefs.AddFieldDef;
      ActiveFieldTypes.InitFieldDef(FieldDef, FieldNo, 'rowid', SQLINT, SIZINT, 0);
      FieldDef.Attributes := [faHiddenCol, faReadonly];
    end;
  finally
    FieldDefs.EndUpdate;
  end;
end;

procedure TIfxTable.DoOnNewRecord;
var
  I: Integer;
begin
  if DataLink.Active then
    for I := 0 to GetMasterFieldList.Count - 1 do
      IndexFields[I] := GetMasterFieldList[I];

  inherited;
end;

function TIfxTable.GetCanModify: Boolean;
begin
  Result := not FReadOnly and inherited GetCanModify;
end;

procedure TIfxTable.UpdateIndexDefs;
var
  IfxSQL: TIfxSQL;
  FirstColumn, LastColumn: Integer;

  procedure InitIndex(Index: TIndexDef; InfoFields: TIfxFields);
  var
    I: Integer;
    Column: Integer;
    FldsSep, DescSep: string;
  begin
    Index.Name := InfoFields[0].AsString;

    if Index.Name = InfoFields[1].AsString then
      Index.Options := Index.Options + [ixPrimary];
    if InfoFields[2].AsString = 'U' then
      Index.Options := Index.Options + [ixUnique];

    for I := FirstColumn to LastColumn do
    begin
      Column := InfoFields[I].AsSmallInt;
      if Column = 0 then Break;

      if Column < 0 then
      begin
        Column := -Column;
        if I = FirstColumn then
          Index.Options := Index.Options + [ixDescending];
        Index.DescFields := Index.DescFields + DescSep + FieldDefs[Column - 1].Name;
        DescSep := ';';
      end;

      Index.Fields := Index.Fields + FldsSep + FieldDefs[Column - 1].Name;
      FldsSep := ';';
    end;
  end;

begin
  FieldDefs.Update;

  FIndexDefs.BeginUpdate;
  try
    FIndexDefs.Clear;

    IfxSQL := CreateTableInfoSQL(TABLE_INDICES_SQL);
    try
      IfxSQL.Macros.Values['onlineindices'] :=
        TABLE_FAMILY_INDEX_COLUMNS[Connection.HasOnlineIndices];
      IfxSQL.Open;
      FirstColumn := TABLE_INDEX_COLNO_BEGIN;
      LastColumn := TABLE_FAMILY_INDEX_COLNO_END[Connection.IsInformixOnline];
      while not IfxSQL.EOF do
      begin
        InitIndex(FIndexDefs.AddIndexDef, IfxSQL.Fields);
        IfxSQL.Next;
      end;
    finally
      IfxSQL.Free;
    end;
  finally
    FIndexDefs.EndUpdate;
  end;
end;

procedure TIfxTable.DefChanged(Sender: TObject);
begin
  FStoreDefs := True;
end;

procedure TIfxTable.SetFilterText(const Value: string);
begin
  if Filter <> Value then
  begin
    inherited;
    if Filtered then
      ResetTableSQL;
  end;
end;

procedure TIfxTable.SetFilterOptions(Value: TFilterOptions);
begin
  if Value <> [] then
    IDACError(SNotSupported, Self);
end;

procedure TIfxTable.PrepareCursor;
begin
  inherited;
  CheckTableSQL;
end;

procedure TIfxTable.FilteredChanged;
begin
  if Filter = '' then
    inherited else
    ResetTableSQL;
end;

function TIfxTable.CreateDataLink: TDataLink;
begin
  Result := TIfxMasterDataLink.Create(Self);
end;

procedure TIfxTable.SetMasterParams;
var
  I: Integer;
begin
  for I := 0 to GetMasterFieldList.Count - 1 do
    IfxSQL.ParamByName(FIndexFields[I]).AssignField(GetMasterFieldList[I]);
end;

{ TIfxTable.IProviderSupport }

function TIfxTable.PSGetDefaultOrder: TIndexDef;
var
  Index: TIndexDef;
begin
  Result := nil;
  try
    if FindDefaultIndex(Index) then
    begin
      GetFieldList(nil, Index.Fields);
      Result := TIndexDef.Create(nil);
      Result.Assign(Index);
    end;
  except
    Result.Free;
  end;
end;

function TIfxTable.PSGetKeyFields: string;

  function UseIndex(Index: TIndexDef; var KeyFields: string): Boolean;
  var
    Pos: Integer;
    IndexFields: string;
  begin
    Result := False;
    IndexFields := Index.Fields;
    Pos := 1;
    while Pos <= Length(IndexFields) do
      if FindField(ExtractFieldName(IndexFields, Pos)) = nil then Exit;
    KeyFields := IndexFields;
    Result := True;
  end;

var
  I: Integer;
  Index: TIndexDef;
begin
  Result := inherited PSGetKeyFields;

  if (Result = '') and (FTableName <> '') then
    try
      FIndexDefs.Update;
      
      if not (FindIndex(ixPrimary, Index) and UseIndex(Index, Result)) then
        for I := 0 to FIndexDefs.Count - 1 do
        begin
          Index := FIndexDefs[I];
          if (ixUnique in Index.Options) and UseIndex(Index, Result) then Break;
        end;
    except
    end;
end;

function TIfxTable.PSGetTableName: string;
begin
  Result := QualifiedTableName;
end;

function TIfxTable.PSGetIndexDefs(IndexTypes: TIndexOptions): TIndexDefs;
begin
  Result := GetIndexDefs(FIndexDefs, IndexTypes);
end;

procedure TIfxTable.PSSetCommandText(const CommandText: string);
begin
  if CommandText <> '' then
    TableName := CommandText;
end;

procedure TIfxTable.GetDetailLinkFields(MasterFields, DetailFields: TList);
var
  I: Integer;
begin
  MasterFields.Clear;
  DetailFields.Clear;
  if (MasterSource <> nil) and (MasterSource.DataSet <> nil) and
   (Self.MasterFields <> '') then
  begin
    MasterSource.DataSet.GetFieldList(MasterFields, Self.MasterFields);
    CheckIndexFields;
    for I := 0 to FIndexFields.Count - 1 do
      DetailFields.Add(FieldByName(FIndexFields[I]));
  end;
end;

procedure TIfxTable.CreateTable;

  function GetFieldsString: string;
  var
    I: Integer;
  begin
    InitFieldDefsFromFields;
    for I := 0 to FieldDefs.Count - 1 do
    begin
      if Result <> '' then
        Result := Result + ', ';
      Result := Result + FieldDefs[I].Name + ' ' +
        ActiveFieldTypes.GetTypeName(FieldDefs[I]);
    end;
  end;

var
  I: Integer;
begin
  CheckInactive;

  CheckConnection.Execute(Format(TABLE_CREATE_SQL, [FTableNameOriginal, GetFieldsString]));

  for I := 0 to FIndexDefs.Count - 1 do
    with FIndexDefs[I] do
      AddIndex(Name, Fields, Options);
end;

procedure TIfxTable.EmptyTable;
begin
  if Active then
    CheckBrowseMode else
    CheckTableName;

  CheckConnection.Execute(Format(TABLE_EMPTY_SQL, [FTableNameOriginal]));

  if Active then
    DataSetImpl.InternalEmpty;
end;

procedure TIfxTable.DeleteTable;
begin
  CheckInactive;
  CheckTableName;
  CheckConnection.Execute(Format(TABLE_DROP_SQL, [FTableNameOriginal]));
end;

procedure TIfxTable.GetIndexNames(List: TStrings);
begin
  FIndexDefs.Update;
  FIndexDefs.GetItemNames(List);
end;

procedure TIfxTable.AddIndex(const Name, Fields: string; Options: TIndexOptions;
  const DescFields: string);
var
  FieldList: string;
  IndexSQL: string;
  IndexName: string;
begin
  if Active then
    CheckBrowseMode;

  FieldDefs.Update;

  if ixPrimary in Options then
    IndexSQL := TABLE_ADD_PRIMARY_KEY_SQL
  else if ixUnique in Options then
    IndexSQL := TABLE_CREATE_UNIQUE_INDEX_SQL
  else
    IndexSQL := TABLE_CREATE_INDEX_SQL;

  if Name[1] = ' ' then
    IndexName := '_' + Trim(Name) else
    IndexName := Name;

  FieldList := FormatFieldList(Fields, DescFields);

  CheckConnection.Execute(Format(IndexSQL, [IndexName, FTableNameOriginal, FieldList]));

  FIndexDefs.Updated := False;
end;

procedure TIfxTable.DeleteIndex(const Name: string);
begin
  if Active then
    CheckBrowseMode;

  CheckConnection.Execute(Format(TABLE_DROP_INDEX_SQL, [Name]));
  
  FIndexDefs.Updated := False;
end;

procedure TIfxTable.GotoCurrent(Table: TIfxTable);
begin
  CheckBrowseMode;
  Table.CheckBrowseMode;

  if (Connection <> Table.Connection) or (CompareText(TableName, Table.TableName) <> 0) then
    IDACError(STableMismatch, Self);

  DoBeforeScroll;
  try
    InternalFirst;
    MoveToRowID(Table.RowID);
  finally
    Resync([]);
    DoAfterScroll;
  end;
end;


end.
